/**
 * Moondance UML modeling tool
 * 
 * This program is free software; you can redistribute it and
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 3
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

package ro.pub.cs.se.moondance.command;

import java.util.ArrayList;
import java.util.Arrays;

import org.eclipse.gef.commands.Command;

import ro.pub.cs.se.moondance.directedit.ForbiddenIdentifierNames;
import ro.pub.cs.se.moondance.model.ClassFunction;
import ro.pub.cs.se.moondance.model.VisibilityType;
import ro.pub.cs.se.moondance.model.Parameter;

/**
 * Command to change the name and type of an attribute
 */
public class ChangeOperationNameTypeCommand extends Command {

	private ClassFunction operation;
	private String name, returnType, visibility;
	private ArrayList<Parameter> parameters = new ArrayList<Parameter>();
	private boolean isStatic, isAbstract, isConstructor, isDestructor;
	private String oldName, oldReturnType, oldVisibility;
	private ArrayList<Parameter> oldParameters = new ArrayList<Parameter>();
	private boolean oldIsStatic, oldIsAbstract, oldIsConstructor,
			oldIsDestructor;
	private static String patterns[] = { "<<constructor>>", "<<destructor>>",
			"<<static>>", "<<abstract>>" };
	private ArrayList<String> patternsList;

	public ChangeOperationNameTypeCommand(ClassFunction operation) {
		this.operation = operation;
		patternsList = new ArrayList<String>(Arrays.asList(patterns));
	}

	/**
	 * @see org.eclipse.gef.commands.Command#execute()
	 */
	public void execute() {
		operation.setName(name);
		operation.setReturnType(returnType);
		operation.setVisibility(visibility);
		operation.setStatic(isStatic);
		operation.setAbstract(isAbstract);
		operation.setConstructor(isConstructor);
		operation.setDestructor(isDestructor);
		operation.setParameters(parameters);
	}

	/**
	 * @return whether we can apply changes
	 */
	public boolean canExecute() {
		if (name != null && returnType != null && visibility != null) {
			if (isConstructor || isDestructor) {
				if (name.equals(operation.getParent().getName()))
					return true;
				else {
					name = oldName;
					returnType = oldReturnType;
					visibility = oldVisibility;
					isStatic = oldIsStatic;
					isAbstract = oldIsAbstract;
					isConstructor = oldIsConstructor;
					isDestructor = oldIsDestructor;
					parameters.clear();
					parameters.addAll(oldParameters);
					return false;
				}
			} else
				return true;
		} else {
			name = oldName;
			returnType = oldReturnType;
			visibility = oldVisibility;
			isStatic = oldIsStatic;
			isAbstract = oldIsAbstract;
			isConstructor = oldIsConstructor;
			isDestructor = oldIsDestructor;
			parameters.clear();
			parameters.addAll(oldParameters);
			return false;
		}
	}

	/**
	 * Sets the new Column name
	 * 
	 * @param string
	 *            the new name
	 */
	public void setConfigurationString(String string) {
		name = null;
		returnType = null;
		visibility = null;
		parameters.clear();
		isStatic = false;
		isAbstract = false;
		isConstructor = false;
		isDestructor = false;

		String firstPart = null, secondPart = null;
		int openBracketIndex, closedBracketIndex;
		String params = "";

		if ((string != null) && (string.length() > 0)) {
			int colonIndex = string.lastIndexOf(':');
			openBracketIndex = string.indexOf('(');
			closedBracketIndex = string.indexOf(')');
			if ((openBracketIndex >= 0) && (closedBracketIndex >= 0))
				if (closedBracketIndex == string.length() - 1) {// return type
																// missing
					firstPart = string;
					secondPart = "";
				} else if ((colonIndex >= 0)
						&& (colonIndex > closedBracketIndex)) {
					firstPart = string.substring(0, colonIndex);
					secondPart = string.substring(colonIndex + 1);
				} else
					return;

			if ((firstPart != null) && (secondPart != null)) {
				params = firstPart.substring(openBracketIndex + 1,
						closedBracketIndex);
				firstPart = firstPart.substring(0, openBracketIndex);

				String[] result = firstPart.split("\\s");
				// visibility <<constructor>>/<<destructor>> <<static>>
				// <<abstract>>
				// name(parameters) : returnType

				switch (result.length) {
				case 1:// name
					if (!ForbiddenIdentifierNames.contains(result[0])
							&& (!(result[0].startsWith("<<") && result[0]
									.endsWith(">>")))) {
						name = result[0];
						visibility = VisibilityType.PUBLIC;
					}
					break;
				case 2: // visibility name || pattern name
					if (result[0].startsWith("<<") && result[0].endsWith(">>")) {// pattern
						if (!ForbiddenIdentifierNames.contains(result[1])) {
							String pattern = result[0].substring(2, result[0]
									.length() - 2);
							if (pattern.equals("static"))
								isStatic = true;
							else if (pattern.equals("abstract"))
								isAbstract = true;
							else if (pattern.equals("constructor"))
								isConstructor = true;
							else if (pattern.equals("destructor"))
								isDestructor = true;
							else
								return;

							name = result[1];
							visibility = VisibilityType.PUBLIC;
						}
					} else {// visibility
						if (VisibilityType.contains(result[0])) {
							if (!ForbiddenIdentifierNames.contains(result[1])) {
								visibility = result[0];
								name = result[1];
							}
						}
					}
					break;

				case 3: // visibility pattern name | pattern pattern name
					if (result[0].startsWith("<<") && result[0].endsWith(">>")) {// pattern
						if (result[1].startsWith("<<")
								&& result[1].endsWith(">>")) {// second
							// pattern
							if (result[0].equals("<<abstract>>")
									&& result[1].equals("<<destructor>>")) {
								if (!ForbiddenIdentifierNames
										.contains(result[2])) {
									name = result[2];
									visibility = VisibilityType.PUBLIC;
									isAbstract = isDestructor = true;
								}
							}
						}
					} else {
						if (VisibilityType.contains(result[0])) {
							if (patternsList.contains(result[1])) {
								if (!ForbiddenIdentifierNames
										.contains(result[2])) {
									name = result[2];
									visibility = result[0];
									String pattern = result[1].substring(2,
											result[1].length() - 2);
									if (pattern.equals("abstract"))
										isAbstract = true;
									else if (pattern.equals("static"))
										isStatic = true;
									else if (pattern.equals("constructor"))
										isConstructor = true;
									else if (pattern.equals("destructor"))
										isDestructor = true;
								}
							}
						}
					}
					break;
				case 4: // visibility pattern pattern name
					if (VisibilityType.contains(result[0])) {
						if (result[1].startsWith("<<")
								&& result[1].endsWith(">>")) {// pattern
							if (result[2].startsWith("<<")
									&& result[2].endsWith(">>")) {// second
								// pattern
								if (result[1].equals("<<abstract>>")
										&& result[2].equals("<<destructor>>")) {
									if (!ForbiddenIdentifierNames
											.contains(result[3])) {
										name = result[3];
										visibility = result[0];
										isAbstract = isDestructor = true;
									}
								}
							}
						}
					}
					break;

				}

				if (params.length() > 0) {
					String[] splitParameters = params.split(",");
					for (int i = 0; i < splitParameters.length; i++) {
						String[] nameType = splitParameters[i].split(":");
						if (!ForbiddenIdentifierNames.contains(nameType[0])) {
							Parameter newParameter = new Parameter();
							newParameter.setName(nameType[0]);
							if (nameType[1].endsWith("[]")) {
								newParameter.setMultiplicity(true);
								newParameter.setType(nameType[1].substring(0,
										nameType[1].length() - 2));
							} else {
								newParameter.setMultiplicity(false);
								newParameter.setType(nameType[1]);
							}
							parameters.add(newParameter);
						} else
							return;
					}
				}

				if (isConstructor || isDestructor)
					returnType = "";
				else if (secondPart.length() > 0)
					returnType = secondPart;

			}
		}
	}

	/**
	 * Methods to set the old attribute values
	 */
	public void setOldName(String string) {
		oldName = string;
	}

	public void setOldReturnType(String string) {
		oldReturnType = string;
	}

	public void setOldVisibility(String string) {
		oldVisibility = string;
	}

	public void setOldStatic(boolean isStatic) {
		oldIsStatic = isStatic;
	}

	public void setOldAbstract(boolean isAbstract) {
		oldIsAbstract = isAbstract;
	}

	public void setOldConstructor(boolean isConstructor) {
		oldIsConstructor = isConstructor;
	}

	public void setOldDestructor(boolean isDestructor) {
		oldIsDestructor = isDestructor;
	}

	public void setOldParameters(ArrayList<Parameter> parameters) {
		oldParameters.clear();
		oldParameters.addAll(parameters);
	}

	/**
	 * @see org.eclipse.gef.commands.Command#undo()
	 */
	public void undo() {
		operation.setName(oldName);
		operation.setReturnType(oldReturnType);
		operation.setVisibility(oldVisibility);
		operation.setStatic(oldIsStatic);
		operation.setAbstract(oldIsAbstract);
		operation.setConstructor(oldIsConstructor);
		operation.setDestructor(oldIsDestructor);
	}

}